﻿/*
 * User: artem
 * Date: 27.04.2011
 * Time: 20:18
 * 
 */
using System;
using System.Collections.Generic;
using System.IO;

using Kernel;
using CacheObjects;

using OOExp = Kernel.OpenOrganizerException;
using OOECode = Kernel.OpenOrganizerException.ErrorCode;

namespace Client.Logic
{
	/// <summary>
	/// Контроллер клиента.
	/// Через него проходят все операции к бд.
	/// В основном выполняет функции конвертирования.
	/// </summary>
	public sealed class Controller : DataBaseService
	{
		/// <summary>
		/// Ссылка на интерфейс клиента для работы с бд.
		/// </summary>
		private IDBClient data_base;

        /// <summary>
        /// Кеш объектов
        /// </summary>
        private CacheObjects<int, Event> cache;

		/// <summary>
		/// Реализация синглетона или одиночки.
		/// </summary>
		private static Controller instance = new Controller();
		
		public static Controller Instance 
		{	get { return instance; }	}
		
		private Controller() 
		{	
			try
			{
				data_base = DataBase.Instance as IDBClient;
				cache = new CacheObjects<int, Event>();
			}
			catch (OOExp exp)
            { throw new OOExp(OOECode.StartUpError, exp); }
            catch (Exception exp)
            { throw new OOExp(OOECode.UndefinedError, exp); }

            Action dlg = new Action(() =>
                {
                    try
                    { this.PerfomeEventService(data_base as IDBService); }
                    catch (Exception exp)
                    { Client.GUI.ErrorShower.Show(exp); }
                });

            dlg.BeginInvoke(null, null);
		}
		
		/// <summary>
		/// Получение списка событий за заданный период.
		/// </summary>
		/// <param name="from">
		/// 	начал периода.
		/// </param>
		/// <param name="to">
		/// 	конец периода
		/// </param>
		/// <returns>
		/// 	возвращает список массивов строк для
		/// 	отображения в таблице.
		/// </returns>
		public List<string[]> GetEvents(DateTime from,
		                                DateTime to)
		{
			try
			{
				var result = data_base.GetEvents(from,
				                                 to);

                foreach (var it in result)
                    if (!cache.ContainsKey(it.ID))
                        cache.Add(it.ID, it);
				
				return result.ConvertAll<string[]>((Kernel.Event x) =>
					{
				    	return x.ToStringArray();
				    });
			}
            catch (OOExp exp)
            { throw new OOExp(OOECode.CanNotGetEvents, exp); }
            catch (Exception exp)
            { throw new OOExp(OOECode.UndefinedError, exp); }
		}
		
		/// <summary>
		/// Возвращает евент по его иду.
		/// </summary>
		/// <param name="id">
		/// 	собственно ид
		/// </param>
		/// <returns>
		/// 	собственно эвент.
		/// 	Возвращается не сам эвент,
		/// 	а анонимный тип построенный на 
		/// 	основании эвента.
		/// </returns>
		public dynamic GetEvent(int id)
		{
            try
            {
                Kernel.Event result = cache.ContainsKey(id) ?
                                                        cache[id] :
                                                        data_base.GetEvent(id);

                return new
                    {
                        Title = result.Title,
                        Description = result.Description,
                        Date = result.Date,
                        Type = result.Type,
                        Periodicity = result.Periodicity,
                        NotificationStart = result.NotificationStart,
                        NotificationEnd = result.NotificationEnd,
                        NotificationCount = result.NotificationCount,
                    };
            }
            catch (OOExp exp)
            { throw new OOExp(OOECode.CanNotGetEvent, exp); }
            catch (Exception exp)
            { throw new OOExp(OOECode.UndefinedError, exp); }
		}
		
		/// <summary>
		/// Добавляет новый эвент в бд.
		/// </summary>
		/// <param name="new_event">
		/// 	новый эвент. в качестве которого
		/// 	опять же принимается анонимный тип 
		/// 	от гуев.
		/// </param>
		public void AddEvent(dynamic new_event)
		{
            try
            {
                Kernel.Event adding = new Event();

                adding.Title = new_event.Title;
                adding.Description = new_event.Description;
                adding.Date = new_event.Date;
                adding.Type = (Kernel.Event.EventType)new_event.Type;
                adding.Periodicity = (Kernel.Event.EventPeriodicity)new_event.Periodicity;
                adding.NotificationStart = new_event.NotificationStart;
                adding.NotificationEnd = new_event.NotificationEnd;
                adding.NotificationCount = (ushort)new_event.NotificationCount;

                cache.Add(data_base.AddEvent(adding), adding);
            }
            catch (OOExp exp)
            { throw new OOExp(OOECode.CanNotAddEvent, exp); }
            catch (Exception exp)
            { throw new OOExp(OOECode.UndefinedError, exp); }
		}
		
		/// <summary>
		/// Обновляет эвент по иду.
		/// </summary>
		/// <param name="id">
		/// 	ид редактируемого эвента.
		/// </param>
		/// <param name="updated_event">
		/// 	сам эвент в виде анонимного типа из гуев.
		/// </param>
		public void UpdateEvent(int id, dynamic updated_event)
		{
			try
			{
				Kernel.Event updated = cache.ContainsKey(id) ? 
                                            cache[id] : 
                                            data_base.GetEvent(id);
				
				updated.Title = updated_event.Title;
				updated.Description = updated_event.Description;
				updated.Date = updated_event.Date;
				updated.Type = (Kernel.Event.EventType)updated_event.Type;
				updated.Periodicity = (Kernel.Event.EventPeriodicity)updated_event.Periodicity;
				updated.NotificationStart = updated_event.NotificationStart;
				updated.NotificationEnd = updated_event.NotificationEnd;
				updated.NotificationCount = (ushort)updated_event.NotificationCount;
				
				data_base.UpdateEvent(updated);

                if (!cache.ContainsKey(id))
                    cache.Add(id, updated);
			}
            catch (OOExp exp)
            { throw new OOExp(OOECode.CanNotUpdateEvent, exp); }
            catch (Exception exp)
            { throw new OOExp(OOECode.UndefinedError, exp); }
		}

        public void DeleteEvent(int id)
        {
            try
            {
                data_base.RemoveEvent(id);

                if (cache.ContainsKey(id))
                    cache.Remove(id);
            }
            catch (OOExp exp)
            { throw new OOExp(OOECode.CanNotDeleteEvent, exp); }
            catch (Exception exp)
            { throw new OOExp(OOECode.UndefinedError, exp); }
        }

        public string BuildSchedule(DateTime from, DateTime to)
        {
            const string title_format = "Schedule: {0} - {1}\n\n";
            try
            {
                var events = data_base.GetEvents(from, to);

                string result = String.Format(title_format,
                                              from.ToLongDateString(),
                                              to.ToLongDateString());

                foreach (var it in events)
                    result += it.ToString();

                return result;
            }
            catch (OOExp exp)
            { throw new OOExp(OOECode.CanNotMakeSchedule, exp); }
            catch (Exception exp)
            { throw new OOExp(OOECode.UndefinedError, exp); }
        }

        public void SaveSchedule(string schedule_text, Stream file)
        {
            const char line_separator = '\n';

            try
            {
                using (StreamWriter writer = new StreamWriter(file))
                {
                    //hack, may be need using over stream
                    foreach (var it in schedule_text.Split(line_separator))
                        writer.WriteLine(it);

                    writer.Flush();
                }
            }
            catch (Exception exp)
            { throw new OOExp(OOECode.ScheduleSavingFail, exp); };
        }

        public void PrintSchedule(string schedule_text)
        {
            try
            {
                Printer printer = new Printer(schedule_text);
                printer.Print();
            }
            catch (Exception exp)
            { throw new OOExp(OOECode.SchedulePrintFail, exp); }
        }
	}
}
